#pragma once
#include "execution_defs.h"
#include "execution_manager.h"
#include "executor_abstract.h"
#include "index/ix.h"
#include "system/sm.h"

class NestedLoopJoinExecutor : public AbstractExecutor {
   private:
    std::unique_ptr<AbstractExecutor> left_;
    std::unique_ptr<AbstractExecutor> right_;
    size_t len_;
    std::vector<ColMeta> cols_;

    std::map<TabCol, Value> prev_feed_dict_;

   public:
    NestedLoopJoinExecutor(std::unique_ptr<AbstractExecutor> left, std::unique_ptr<AbstractExecutor> right) {
        // 设置左右孩子
        left_ = std::move(left);
        right_ = std::move(right);
        // 得到连接(笛卡尔积)结果元组的长度
        len_ = left_->tupleLen() + right_->tupleLen();
        // 默认以左孩子作为outer table
        cols_ = left_->cols();
        // inner table col
        auto right_cols = right_->cols();
        for (auto &col : right_cols) {
            col.offset += left_->tupleLen();
        }
        cols_.insert(cols_.end(), right_cols.begin(), right_cols.end());
    }

    std::string getType() override { return "Join"; }

    size_t tupleLen() const override { return len_; }

    const std::vector<ColMeta> &cols() const override { return cols_; }

    void beginTuple() override {
        // 负责初始化左表和右表的扫描，并确保两个表都有记录可以进行连接。
        left_->beginTuple();// 开始扫描左表（或左侧算子）
        if (left_->is_end()) {// 如果左表没有更多记录，直接返回
            return;
        }
        feed_right();// 准备右表的条件，根据左表当前记录的数据
        right_->beginTuple();// 开始扫描右表（或右侧算子）
        while (right_->is_end()) {// 如果右表没有更多记录可供扫描
            // 此处任务点取消,不用做
            // lab3 task2 Todo
            // 如果当前innerTable(右表或算子)扫描完了
            // 你需要移动到outerTable(左表)下一个记录,然后把右表移动到第一个记录的位置
            // lab3 task2 Todo end
            left_->nextTuple();// 移动到左表的下一个记录
            if (left_->is_end())// 如果左表也没有更多记录，结束循环
            {
                break;
            }
            feed_right();// 再次准备右表的条件，根据左表新的当前记录的数据
            right_->beginTuple();// 重新开始扫描右表
        }
    }

    void nextTuple() override {
        // 通过在右表（或右侧算子）上移动到下一个元组，并在必要时将左表（或左侧算子）移动到下一个元组并重置右表扫描，来完成连接操作。
        assert(!is_end());// 断言当前没有到达扫描的末尾
        right_->nextTuple();// 在右侧表（或算子）上移动到下一个元组
        while (right_->is_end()) {// 当右侧表（或算子）的扫描到达末尾时
            // lab3 task2 Todo
            // 如果右节点扫描完了
            // 你需要把左表移动到下一个记录并把右节点回退到第一个记录
            // lab3 task2 Todo end
            left_->nextTuple();// 在左侧表（或算子）上移动到下一个元组
            if (left_->is_end())// 如果左侧表（或算子）也到达末尾，结束函数
            {
                return;
            }
            feed_right();// 将左侧表的当前元组数据用于右侧表的扫描条件
            right_->beginTuple();// 重置右侧表（或算子）的扫描，从头开始
        }
    }

    bool is_end() const override { return left_->is_end(); }

    std::unique_ptr<RmRecord> Next() override {
        // 负责获取左右操作符（算子）的下一个记录，并将这些记录拼接成一个新的记录以返回。
        assert(!is_end());// 断言当前没有到达扫描的末尾
        auto record = std::make_unique<RmRecord>(len_);// 创建一个新的记录对象，长度为len_（左表记录长度加右表记录长度）
        // lab3 task2 Todo
        // 你需要调用左右算子的Next()获取下一个记录进行拼接赋给返回的连接结果std::make_unique<RmRecord>record中
        // memecpy()可能对你有所帮助
        // lab3 task2 Todo End
        std::unique_ptr<RmRecord> left_record = left_->Next();// 获取左表（或左侧算子）的下一条记录
        std::unique_ptr<RmRecord> right_record = right_->Next();// 获取右表（或右侧算子）的下一条记录
        memcpy(record->data, left_record->data, left_->tupleLen());// 将左表记录的数据复制到新记录对象的相应位置
        memcpy(record->data + left_->tupleLen(), right_record->data, right_->tupleLen());// 将右表记录的数据复制到新记录对象的剩余位置
        return record;// 返回拼接后的新记录对象
    }

    // 递归更新条件谓词
    void feed(const std::map<TabCol, Value> &feed_dict) override {
        prev_feed_dict_ = feed_dict;
        left_->feed(feed_dict);
    }

    // 默认以right 作inner table
    void feed_right() {
        // 将左子算子的ColMeta数组和对应的下一个元组转换成<TabCol,Value>的map
        // 每一个表列和其对应的Value相对应
        auto left_dict = rec2dict(left_->cols(), left_->Next().get());
        auto feed_dict = prev_feed_dict_;
        // 将左子算子的<列,值>map中的KV对增加到prev_feed_dict_(feed_dict)中
        feed_dict.insert(left_dict.begin(), left_dict.end());
        // 右子算子调用feed,对当前的feed_dict
        // 重置右子算子的连接条件
        right_->feed(feed_dict);
    }

    Rid &rid() override { return _abstract_rid; }
};